package com.companyname.demo.cod.util;

import java.io.*;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

@Slf4j
public class RSAUtil {
    private static final String ALGORITHOM = "RSA";
    private static final String RSA_PAIR_FILENAME = "./__RSA_PAIR.txt";
    private static final int KEY_SIZE = 1024;
    private static final Provider DEFAULT_PROVIDER = new BouncyCastleProvider();
    private static KeyPairGenerator keyPairGenerator = null;
    private static KeyFactory keyFactory = null;
    private static KeyPair oneKeyPair = null;
    private static File rsaPairFile = new File(RSA_PAIR_FILENAME);

    static {
        try {
            keyPairGenerator = KeyPairGenerator.getInstance("RSA",DEFAULT_PROVIDER);
            keyFactory = KeyFactory.getInstance("RSA",DEFAULT_PROVIDER);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    private RSAUtil() {
    }

    public static void inputstreamToFile(InputStream ins, File file) throws IOException {
        OutputStream os = new FileOutputStream(file);
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        ins.close();
    }

    private static synchronized KeyPair generateKeyPair() {
        try {
            keyPairGenerator.initialize(1024, new SecureRandom(MultiThreadDateUtil.format(new Date(), "yyyyMMdd").getBytes("UTF-8")));
            oneKeyPair = keyPairGenerator.generateKeyPair();
            saveKeyPair(oneKeyPair);
            return oneKeyPair;
        } catch (InvalidParameterException var1) {
            log.error("KeyPairGenerator does not support a key length of 1024.", var1);
        } catch (NullPointerException var2) {
            log.error("RSAUtil#KEY_PAIR_GEN is null, can not generate KeyPairGenerator instance.", var2);
        } catch (Exception var3) {
            log.error("error", var3);
        }

        return null;
    }

    private static String getRSAPairFilePath() {
        String urlPath = RSAUtil.class.getResourceAsStream(RSA_PAIR_FILENAME).toString();
        return urlPath;
    }

    private static boolean isCreateKeyPairFile() {
        boolean createNewKeyPair = false;
        if (!rsaPairFile.exists() || rsaPairFile.isDirectory()) {
            createNewKeyPair = true;
        }
        return createNewKeyPair;
    }

    /**
     * 这里保存密钥对，是因为decryptString()方法解密时需要获取该公钥对应的私钥
     */
    private static void saveKeyPair(KeyPair keyPair) {
        FileOutputStream fileOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
            fileOutputStream = FileUtils.openOutputStream(rsaPairFile);
            objectOutputStream = new ObjectOutputStream(fileOutputStream);
            objectOutputStream.writeObject(keyPair);
        } catch (Exception var7) {
            var7.printStackTrace();
        } finally {
            IOUtils.closeQuietly(objectOutputStream);
            IOUtils.closeQuietly(fileOutputStream);
        }

    }

    public static KeyPair getKeyPair() {
        if (isCreateKeyPairFile()) {
            return generateKeyPair();
        } else {
            //服务重新启动时，oneKeyPair为null，就需要读取密钥对文件
            return oneKeyPair != null ? oneKeyPair : readKeyPair();
        }
    }

    private static KeyPair readKeyPair() {
        FileInputStream fileInputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = FileUtils.openInputStream(rsaPairFile);
            objectInputStream = new ObjectInputStream(fileInputStream);
            oneKeyPair = (KeyPair) objectInputStream.readObject();
            KeyPair var2 = oneKeyPair;
            return var2;
        } catch (Exception var6) {
            var6.printStackTrace();
        } finally {
            IOUtils.closeQuietly(objectInputStream);
            IOUtils.closeQuietly(fileInputStream);
        }

        return null;
    }

    public static RSAPublicKey generateRSAPublicKey(byte[] modulus, byte[] publicExponent) {
        RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(publicExponent));

        try {
            return (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
        } catch (InvalidKeySpecException var4) {
            log.error("RSAPublicKeySpec is unavailable.", var4);
        } catch (NullPointerException var5) {
            log.error("RSAUtil#KEY_FACTORY is null, can not generate KeyFactory instance.", var5);
        }

        return null;
    }

    public static RSAPrivateKey generateRSAPrivateKey(byte[] modulus, byte[] privateExponent) {
        RSAPrivateKeySpec privateKeySpec = new RSAPrivateKeySpec(new BigInteger(modulus), new BigInteger(privateExponent));

        try {
            return (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
        } catch (InvalidKeySpecException var4) {
            log.error("RSAPrivateKeySpec is unavailable.", var4);
        } catch (NullPointerException var5) {
            log.error("RSAUtil#KEY_FACTORY is null, can not generate KeyFactory instance.", var5);
        }

        return null;
    }

    public static RSAPrivateKey getRSAPrivateKey(String hexModulus, String hexPrivateExponent) {
        if (!StringUtils.isEmpty(hexModulus) && !StringUtils.isEmpty(hexPrivateExponent)) {
            byte[] modulus = null;
            byte[] privateExponent = null;

            try {
                modulus = Hex.decodeHex(hexModulus.toCharArray());
                privateExponent = Hex.decodeHex(hexPrivateExponent.toCharArray());
            } catch (DecoderException var5) {
                log.error("hexModulus or hexPrivateExponent value is invalid. return null(RSAPrivateKey).");
            }

            return modulus != null && privateExponent != null ? generateRSAPrivateKey(modulus, privateExponent) : null;
        } else {
            if (log.isDebugEnabled()) {
                log.debug("hexModulus and hexPrivateExponent cannot be empty. RSAPrivateKey value is null to return.");
            }

            return null;
        }
    }

    public static RSAPublicKey getRSAPublidKey(String hexModulus, String hexPublicExponent) {
        if (!StringUtils.isBlank(hexModulus) && !StringUtils.isBlank(hexPublicExponent)) {
            byte[] modulus = null;
            byte[] publicExponent = null;

            try {
                modulus = Hex.decodeHex(hexModulus.toCharArray());
                publicExponent = Hex.decodeHex(hexPublicExponent.toCharArray());
            } catch (DecoderException var5) {
                log.error("hexModulus or hexPublicExponent value is invalid. return null(RSAPublicKey).");
            }

            return modulus != null && publicExponent != null ? generateRSAPublicKey(modulus, publicExponent) : null;
        } else {
            if (log.isDebugEnabled()) {
                log.debug("hexModulus and hexPublicExponent cannot be empty. return null(RSAPublicKey).");
            }

            return null;
        }
    }

    public static byte[] encrypt(PublicKey publicKey, byte[] data) throws Exception {
        Cipher ci = Cipher.getInstance("RSA",DEFAULT_PROVIDER);
        ci.init(1, publicKey);
        return ci.doFinal(data);
    }

    public static byte[] decrypt(PrivateKey privateKey, byte[] data) throws Exception {
        Cipher ci = Cipher.getInstance("RSA",DEFAULT_PROVIDER);
        ci.init(2, privateKey);
        return ci.doFinal(data);
    }

    public static String encryptString(PublicKey publicKey, String plaintext) {
        if (publicKey != null && plaintext != null) {
            try {
                byte[] data = plaintext.getBytes("UTF-8");
                byte[] en_data = encrypt(publicKey, data);
                return new String(Hex.encodeHex(en_data));
            } catch (Exception var4) {
                log.error(var4.getCause().getMessage());
                return null;
            }
        } else {
            return null;
        }
    }

    public static String encryptString(String plaintext) {
        if (plaintext == null) {
            return null;
        } else {
            try {
                byte[] data = plaintext.getBytes("UTF-8");
                KeyPair keyPair = getKeyPair();
                byte[] en_data = encrypt((RSAPublicKey) keyPair.getPublic(), data);
                return new String(Hex.encodeHex(en_data));
            } catch (NullPointerException var4) {
                log.error("keyPair cannot be null.");
            } catch (Exception var5) {
                log.error(var5.getCause().getMessage());
            }

            return null;
        }
    }

    public static String decryptString(PrivateKey privateKey, String encrypttext) {
        if (privateKey != null && !StringUtils.isEmpty(encrypttext)) {
            try {
                byte[] en_data = Hex.decodeHex(encrypttext.toCharArray());
                byte[] data = decrypt(privateKey, en_data);
                return new String(data, "UTF-8");
            } catch (Exception var4) {
                log.error(String.format("\"%s\" Decryption failed. Cause: %s", encrypttext, var4.getCause().getMessage()));
                return null;
            }
        } else {
            return null;
        }
    }

    public static String decryptString(String encryptText) {
        if (StringUtils.isEmpty(encryptText)) {
            return null;
        } else {
            KeyPair keyPair = getKeyPair();
            try {
                byte[] en_data = Hex.decodeHex(encryptText.toCharArray());
                byte[] data = decrypt((RSAPrivateKey) keyPair.getPrivate(), en_data);
                return new String(data, "UTF-8");
            } catch (NullPointerException var4) {
                log.error("keyPair cannot be null.");
            } catch (Exception var5) {
                log.error(String.format("\"%s\" Decryption failed. Cause: %s", encryptText, var5.getMessage()));
            }

            return null;
        }
    }

    public static String decryptStringByJs(String encryptText) {
        String text = decryptString(encryptText);
        return text == null ? null : StringUtils.reverse(text);
    }

    public static RSAPublicKey getDefaultPublicKey() {
        KeyPair keyPair = getKeyPair();
        return keyPair != null ? (RSAPublicKey) keyPair.getPublic() : null;
    }

    public static RSAPrivateKey getDefaultPrivateKey() {
        KeyPair keyPair = getKeyPair();
        return keyPair != null ? (RSAPrivateKey) keyPair.getPrivate() : null;
    }

    public static Map<String, Object> getPublicKeyMap() {
        Map<String, Object> publicKeyMap = new HashMap();
        RSAPublicKey rsaPublicKey = getDefaultPublicKey();
        //系数
        publicKeyMap.put("modulus", new String(Hex.encodeHex(rsaPublicKey.getModulus().toByteArray())));
        //指数
        publicKeyMap.put("exponent", new String(Hex.encodeHex(rsaPublicKey.getPublicExponent().toByteArray())));
        return publicKeyMap;
    }
}


